enum sexEnum { Women = 0, Men }

enum gradeEnum { A = 1, B, C, D, E }

interface User {
  id: number
  name: string
  age: number
  sex: sexEnum
}

interface Student {
  id: number
  grade: gradeEnum
}

// 泛型
// 泛型的目的是为了创建可重用的组件，一个组件可以支持多种类型的数据，这样用户就可以以自己的数据类型来使用组件。

// 例如：
/* 
  需求：
  声明一个方法，传入任何数据，导出传入值，且类型一致
*/

// 不使用泛型的方法
function idenFoo(arg: string): string {
  return arg
}
// ... 多个一样的方法，或者使用函数重载

// 使用any类型
function identFooAny(arg: any): any {
  return arg
}

// --------------------------------------

// 使用泛型方法
function identFoo<T>(arg: T): T {
  return arg
}

// 多个泛型参数的函数
function ff<K, V>(a: K, b: V): [K, V] {
  return [a, b]
}

const bb = <K>(a: K): K => {
  return a
}

function ff1<K, V>(a: K, b: V): string {
  return a + '' + b
}

// 泛型接口
interface idenUserInfo<T> {
  data: T[]
  add: (t: T) => void
  getById: (id: number) => T | undefined  // getById 会返回 undefined
}

class idenUser {
  id?: number
  name: string
  age: number

  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}

class UserCURD implements idenUserInfo<idenUser>{
  data: idenUser[] = []

  add(user: idenUser): void {
    user = { ...user, id: Date.now() }
    this.data.push(user)
    console.log('保存user', user.id);
  }

  getById(id: number): idenUser | undefined {
    return this.data.find(item => item.id === id)
  }
}

const user1 = new UserCURD()
user1.add(new idenUser('Tom', 12))
user1.add(new idenUser('Bob', 16))
console.log(user1.data);

// 具名函数 泛型尝试
const fanFooFunc1: <K>(x: K, y: K) => string = <K>(x: K, y: K): string => {
  return x + '' + y
}
const fanFooFunc2: <K>(x: K, y: K) => string = function <K>(x: K, y: K): string {
  return x + '' + y
}
// 泛型函数引入接口
interface fanFoo {
  <K>(x: K, y: K): string
}
const fanFooFunc3: fanFoo = function <K>(x: K, y: K): string {
  return x + '' + y
}

// 泛型类
class idenClass<T> {
  zeroValue!: T
  add!: (x: T, y: T) => T
}

let myIdenNum = new idenClass<number>()
myIdenNum.zeroValue = 0
myIdenNum.add = function (x, y) {
  return x + y
}

let myIdenStr = new idenClass<string>()
myIdenStr.zeroValue = 'adad'
myIdenStr.add = function (x, y) {
  return x + y
}

console.log(myIdenNum.add(myIdenNum.zeroValue, 333));
console.log(myIdenStr.add(myIdenStr.zeroValue, 'TEST'));

// 泛型约束
// 场景：如果我们直接怼一个泛型参数取 length 属性，会报错，因为这个泛型根本不知道它有这个属性
// 如：报错场景
function fn<T>(x: T): void {
  // console.log(x.length); // Error x 上不存在 length
}

// 引入约束来实现这个需求
interface LengthWise {
  length: number
}
function fn2<T extends LengthWise>(x: T): void {
  console.log(x.length);
}

// fn2(11)   // 报错
fn2([1, 2, 3])
fn('111111')
